/**
 * 操控树形结构公共函数方法
 * findTreeData     筛选多维树形结构 返回查询到的第一个结果
 * filterTreeData   筛选多维树形结构 返回查询到的结果数组
 * mapTreeData      处理多维树形结构数组的每个元素，并返回处理后的数组
 * getTreeData      一维树形结构转多维树形结构
 * flattenTreeData  多维树形结构转一维树形结构
 */

/** 配置项 */
type TreeDataConfig = {
  /** 唯一标识 默认是id */
  key?: string
  /** 与父节点关联的唯一标识 默认是pId */
  parentKey?: string
  /** 查询子集的属性名 默认是children */
  childrenName?: string
  /** 返回值是否为一维树形结构  默认是false*/
  isTileArray?: boolean
  /** 是否添加私有变量 默认是false */
  isSetPrivateKey?: boolean
}

type TreeNode = {
  _pId?: string | number
  _pathArr?: Array<string | number>
}

/**
 * 新增业务参数。
 * _pId     父级id
 * _pathArr 记录了从一级到当前节点的id集合。
 * _pathArr 的length可以记录当前是多少层
 * @param treeNode
 * @param parentTreeNode
 * @param treeDataConfig
 */
const setPrivateKey = <T extends { [x: string]: any }>(
  treeNode: T & TreeNode,
  parentTreeNode: (T & TreeNode) | undefined,
  config?: TreeDataConfig
) => {
  const { key = `id` } = config || {}
  treeNode._pId = parentTreeNode?.[key]
  treeNode._pathArr = parentTreeNode?._pathArr
    ? [...parentTreeNode._pathArr, treeNode[key]]
    : [treeNode[key]]
}

type FindTreeData = <T extends { [x: string]: any }>(
  treeData?: readonly T[],
  callBack?: (treeNode: T, index: number, parentTreeNode?: T) => boolean,
  config?: TreeDataConfig,
  parentTreeNode?: T
) => (T & TreeNode) | undefined
/**
 * 筛选多维树形结构 返回查询到的第一个结果
 * @param treeData 树形结构数组
 * @param callBack  写入搜索条件 用法和数组的find方法一致(前两个参数一样 第三个参数为父级的详情 没有父级的返回undefined)
 * @param config  配置项(key,childrenName,isSetPrivateKey)
 * @returns  返回查询到的第一个结果
 */
export const findTreeData: FindTreeData = (treeData = [], callBack, config, parentTreeNode) => {
  const { childrenName = `children`, isSetPrivateKey = false } = config || {}
  for (const treeNode of treeData) {
    isSetPrivateKey && setPrivateKey(treeNode, parentTreeNode, config)
    if (callBack?.(treeNode, treeData.indexOf(treeNode), parentTreeNode)) {
      return treeNode
    }
    if (treeNode[childrenName]) {
      const dataInfo = findTreeData(treeNode[childrenName], callBack, config, treeNode)
      if (dataInfo) {
        return dataInfo
      }
    }
  }
}

/**
 * 筛选多维树形结构 返回查询到的结果数组
 * @param treeData 树形结构数组
 * @param callBack 写入搜索条件 用法和数组的filter方法一致(前两个参数一样 第三个参数为父级的详情 没有父级的返回undefined)
 * @param config  配置项(key,childrenName,isTileArray,isSetPrivateKey)
 * @returns 返回查询到的结果数组
 */
export const filterTreeData = <T extends { [x: string]: any }>(
  treeData: readonly T[] = [],
  callBack?: (treeNode: T, index: number, parentTreeNode?: T) => boolean,
  config?: TreeDataConfig
): (T & TreeNode)[] => {
  const { childrenName = `children`, isTileArray = false, isSetPrivateKey = false } = config || {}
  const resultTileArr: T[] = []
  const fun = (_treeData: readonly T[], parentTreeNode?: T): T[] => {
    return _treeData.flatMap((treeNode, index) => {
      isSetPrivateKey && setPrivateKey(treeNode, parentTreeNode, config)
      const bool = callBack?.(treeNode, index, parentTreeNode)
      const newNode = { ...treeNode }
      if (treeNode[childrenName]) {
        ;(newNode[childrenName] as T[]) = fun(treeNode[childrenName], newNode)
      }
      if (bool) {
        resultTileArr.push(newNode)
        return newNode
      } else if (newNode[childrenName] && newNode[childrenName].length) {
        return newNode
      } else {
        return []
      }
    })
  }
  const resultArr = fun(treeData)
  return isTileArray ? resultTileArr : resultArr
}

/**
 * 处理多维树形结构数组的每个元素，并返回处理后的数组
 * @param treeData 树形结构数组
 * @param callBack 写入搜索条件 用法和数组的map方法一致(前两个参数一样 第三个参数为旧父级的详情 第四个参数为新父级详情)
 * @param config  配置项(key,childrenName,isTileArray,isSetPrivateKey)
 * @returns 返回查询到的结果数组
 */
export const mapTreeData = <T extends { [x: string]: any }>(
  treeData: readonly T[] = [],
  callBack?: (
    treeNode: T,
    index: number,
    oldParentTreeNode?: T,
    newParentTreeNode?: T
  ) => { [x: string]: any } | any,
  config?: TreeDataConfig
): Array<T & TreeNode & { [x: string]: any }> => {
  const { childrenName = `children`, isTileArray = false, isSetPrivateKey = false } = config || {}
  const resultTileArr: Array<T & { [x: string]: any }> = []
  const fun = (_treeData: readonly T[], oldParentTreeNode?: T, newParentTreeNode?: T) => {
    return _treeData.map((treeNode, index) => {
      isSetPrivateKey && setPrivateKey(treeNode, oldParentTreeNode, config)
      const callBackInfo = callBack?.(treeNode, index, oldParentTreeNode, newParentTreeNode)
      if (isTileArray) {
        resultTileArr.push(callBackInfo)
      }
      const mappedTreeNode = {
        ...treeNode,
        ...callBackInfo,
      }
      if (treeNode?.[childrenName]) {
        mappedTreeNode[childrenName] = fun(treeNode[childrenName], treeNode, mappedTreeNode)
      }
      return mappedTreeNode
    })
  }
  const resultArr = fun(treeData)
  return isTileArray ? resultTileArr : resultArr
}

/**
 * 一维树形结构转多维树形结构
 * @param tileArray 一维树形结构数组
 * @param config 配置项(key,childrenName,parentKey,isSetPrivateKey)
 * @returns 返回多维树形结构数组
 */
export const getTreeData = <T extends { [x: string]: any }>(
  tileArray: readonly T[] = [],
  config?: TreeDataConfig
): (T & TreeNode)[] => {
  const {
    key = `id`,
    childrenName = `children`,
    parentKey = `pId`,
    isSetPrivateKey = false,
  } = config || {}
  const fun = (parentTreeNode: { [x: string]: any }) => {
    const parentId = parentTreeNode[key]
    const childrenNodeList: T[] = []
    copyTileArray = copyTileArray.filter(item => {
      if (item[parentKey] === parentId) {
        childrenNodeList.push({ ...item })
        return false
      } else {
        return true
      }
    })
    parentTreeNode[childrenName] = childrenNodeList
    childrenNodeList.forEach(item => {
      isSetPrivateKey && setPrivateKey(item, parentTreeNode, config)
      fun(item)
    })
  }
  const rootNodeList = tileArray.filter(item => !tileArray.some(i => i[key] === item[parentKey]))
  const resultArr: (T & TreeNode)[] = []
  let copyTileArray = [...tileArray]
  rootNodeList.forEach(item => {
    const index = copyTileArray.findIndex(i => i[key] === item[key])
    if (index > -1) {
      copyTileArray.splice(index, 1)
      const obj = { ...item }
      resultArr.push(obj)
      isSetPrivateKey && setPrivateKey(obj, undefined, config)
      fun(obj)
    }
  })
  return resultArr
}

/**
 * 多维树形结构转一维树形结构
 * @param treeData 树形结构数组
 * @param config 配置项(key,childrenName,isSetPrivateKey)
 * @returns 返回一维树形结构数组
 */
export const flattenTreeData = <T extends { [x: string]: any }>(
  treeData: readonly T[] = [],
  config?: TreeDataConfig
): (T & TreeNode)[] => {
  const { childrenName = `children`, isSetPrivateKey = false } = config || {}
  const result: T[] = []
  const fun = (_treeData: readonly T[], parentTreeNode?: T) => {
    _treeData.forEach(treeNode => {
      isSetPrivateKey && setPrivateKey(treeNode, parentTreeNode, config)
      result.push(treeNode)
      if (treeNode[childrenName]) {
        fun(treeNode[childrenName], treeNode)
      }
    })
  }
  fun(treeData)
  return result
}
